﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Djs.Neuronic.Core;

namespace Djs.Neuronic.Brain
{
    #region SpeechTextProcessor : Třída která zpracovává vstupující text (dlouhý string) na sadu vět a slov.
    /// <summary>
    /// SpeechTextProcessor : Třída která zpracovává vstupující text (dlouhý string) na sadu vět a slov.
    /// </summary>
    public class SpeechTextProcessor
    {
        public SpeechTextProcessor(int threadNumber)
        {
            PrepareDelimiters();
        }
        /// <summary>
        /// Příznak true, pokud tento procesor je možno odstranit. 
        /// Značí, že procesor není aktivní (nepracuje a ani nemá nezpracovaný zůstatek textu).
        /// </summary>
        public bool IsEmpty { get { return (this._TextBuffer == null && !this._NowProcessing); } }

        public List<SpeechSentenceCls> ProcessText(string text, bool isWhole)
        {
            try
            {
                this._NowProcessing = true;
                this._ProcessText(text, false);
            }
            finally
            {
                this._NowProcessing = false;
            }
            return this._SentenceList;
        }
        /// <summary>
        /// Příprava oddělovačů, volá se jedenkrát při initu
        /// </summary>
        private void PrepareDelimiters()
        {
            string blockDelimiters = ".";
            string spaceCharacters = "\r\n\t";
            string wordDelimiters = " ,;:'!?+-*/=´°§|@#$%^&()[]{}" + "\"\\" + spaceCharacters + blockDelimiters;
            this.BlockDelimiters = blockDelimiters.ToCharArray();
            this.SpaceCharacters = spaceCharacters.ToCharArray();
            this.WordDelimiters = wordDelimiters.ToCharArray();
        }
        /// <summary>
        /// Zpracuje celý text. Vnitřní výkonná rutina.
        /// </summary>
        /// <param name="text"></param>
        private void _ProcessText(string text, bool isWhole)
        {
            this._SentenceList = new List<SpeechSentenceCls>();
            this._CurrentSentence = null;
            this._TextBuffer = (this._TextBuffer == null ? "" : this._TextBuffer) + (text == null ? "" : text);
            this._CurrentIndex = 0;
            this._NextBlockDelimiterIndex = -1;
            this._NextWordDelimiterIndex = -1;
            this._LastIndex = this._TextBuffer.Length - 1;

            while (true) // (this._CurrentIndex <= this._LastIndex)
            {
                // Najdeme pozici nejbližších znaků typu Oddělovač bloků, a Oddělovač slov:
                if (this._NextBlockDelimiterIndex.HasValue && this._NextBlockDelimiterIndex.Value < this._CurrentIndex)
                    this._NextBlockDelimiterIndex = this._TextBuffer.SearchNext(this.BlockDelimiters, this._CurrentIndex);
                if (this._NextWordDelimiterIndex.HasValue && this._NextWordDelimiterIndex.Value < this._CurrentIndex)
                    this._NextWordDelimiterIndex = this._TextBuffer.SearchNext(this.WordDelimiters, this._CurrentIndex);

                if (this._NextBlockDelimiterIndex.HasValue && (!this._NextWordDelimiterIndex.HasValue || this._NextWordDelimiterIndex.Value >= this._NextBlockDelimiterIndex.Value))
                {   // Nejblíže indexu _CurrentIndex je oddělovač bloků:
                    this._AddText(this._NextBlockDelimiterIndex.Value - this._CurrentIndex);
                    this._AddBlockDelimiter(this._NextBlockDelimiterIndex.Value);
                }
                else if (this._NextWordDelimiterIndex.HasValue)
                {   // Nejblíže indexu _CurrentIndex je oddělovač slov:
                    this._AddText(this._NextWordDelimiterIndex.Value - this._CurrentIndex);
                    this._AddWordDelimiter(this._NextWordDelimiterIndex.Value);
                }
                else
                {   // Žádný oddělovač: celý zbytek je slovo, anebo polo-slovo, takže ho nebudu zpracovávat pokud nemusím:
                    if (this._CurrentIndex < this._LastIndex)
                    {   // Ještě je nějaký text?
                        if (isWhole)
                        {   // Parametr tvrdí, že je to celé => zpracuji to:
                            this._AddText(this._LastIndex - this._CurrentIndex + 1);
                            this._CurrentSentence = null;
                            this._TextBuffer = null;
                        }
                        else
                        {   // Parametr říká, že vstupní text nemusí být kompletní, takže text nebudu zpracovávat ale nechám si ho na příště:
                            this._TextBuffer = this._TextBuffer.Substring(this._CurrentIndex);
                        }
                    }
                    else
                    {   // Žádný text nezbyl:
                        this._TextBuffer = null;
                    }
                    break;
                }
            }
        }
        /// <summary>
        /// Do aktuální věty (viz this._GetCurrentSentence()) přidá slovo z bufferu this._TextBuffer,
        /// počínaje znakem na indexu this._CurrentIndex, v délce (len).
        /// Následně index this._CurrentIndex navýší o (len).
        /// V slovu není žádný z oddělovačů.
        /// </summary>
        /// <param name="length"></param>
        private void _AddText(int length)
        {
            if (length > 0)
            {
                SpeechSentenceCls sentence = this._GetCurrentSentence();
                sentence.Add(this._TextBuffer.Substring(this._CurrentIndex, length), SpeechWordType.Text);
                this._CurrentIndex += length;
            }
        }
        /// <summary>
        /// Do aktuální věty (viz this._GetCurrentSentence()) přidá oddělovač slov z pozice (delimiterIndex).
        /// Následně index this._CurrentIndex navýší o (1).
        /// </summary>
        /// <param name="delimiterIndex"></param>
        private void _AddWordDelimiter(int delimiterIndex)
        {
            string delimiter = this._TextBuffer.Substring(this._CurrentIndex, 1);
            this._CurrentIndex += 1;

            if (delimiter.IndexOfAny(this.SpaceCharacters) >= 0)
                delimiter = " ";
            bool isSpace = (delimiter == " ");

            if (isSpace && this.IsBeforeNewSentence)
                // Mezeru na začátek nového řádku dávat nebudu:
                return;

            SpeechSentenceCls sentence = this._GetCurrentSentence();
            if (isSpace && sentence.WordCount > 0 && sentence.LastWord.Text == " ")
                // Mezeru nebudu přidávat ani v případě, že věta už má nějaké slovo, a poslední je mezera (tzn. nebud opakovat mezery za sebou):
                return;

            sentence.Add(delimiter, SpeechWordType.WordDelimiter);
        }
        /// <summary>
        /// Do aktuální věty (viz this._GetCurrentSentence()) přidá oddělovač bloků z pozice (delimiterIndex).
        /// Následně index this._CurrentIndex navýší o (1).
        /// </summary>
        /// <param name="delimiterIndex"></param>
        private void _AddBlockDelimiter(int delimiterIndex)
        {
            string delimiter = this._TextBuffer.Substring(this._CurrentIndex, 1);
            this._CurrentIndex += 1;

            SpeechSentenceCls sentence = this._GetCurrentSentence();
            sentence.Add(delimiter, SpeechWordType.BlockDelimiter);
            this._CurrentSentence = null;
        }
        /// <summary>
        /// Vrátí aktuální větu. Pokud není, pak vytvoří novou, zařadí ji do seznamu a vrátí ji.
        /// </summary>
        /// <returns></returns>
        private SpeechSentenceCls _GetCurrentSentence()
        {
            if (this.IsBeforeNewSentence)
            {
                this._CurrentSentence = new SpeechSentenceCls();
                this._SentenceList.Add(this._CurrentSentence);
            }
            return this._CurrentSentence;
        }
        /// <summary>
        /// true pokud minulá věta je ukončená (nebo nebyla) a nová ještě nezačala
        /// </summary>
        private bool IsBeforeNewSentence { get { return (this._CurrentSentence == null); } }
        #region Dělení textu na věty a slova - podpůrná vrstva


        private char[] BlockDelimiters;
        private char[] SpaceCharacters;
        private char[] WordDelimiters;

        private string _TextBuffer;
        /// <summary>
        /// Index znaku, na němž začíná aktuálně nalezené slovo / delimiter
        /// </summary>
        private int _CurrentIndex;
        /// <summary>
        /// Nejbližší nalezený index znaku BlockDelimiters
        /// </summary>
        private Int32? _NextBlockDelimiterIndex;
        /// <summary>
        /// Nejbližší nalezený index znaku WordDelimiters
        /// </summary>
        private Int32? _NextWordDelimiterIndex;
        /// <summary>
        /// Index posledního znaku
        /// </summary>
        private int _LastIndex;
        /// <summary>
        /// Výsledek jednoho procesu
        /// </summary>
        private List<SpeechSentenceCls> _SentenceList;
        /// <summary>
        /// Aktuální věta, do níž se přidává
        /// </summary>
        private SpeechSentenceCls _CurrentSentence;
        /// <summary>
        /// Příznak, že právě probíhá zpracování. V tom případě property this.IsEmpty hlásí false.
        /// </summary>
        private bool _NowProcessing;

        #endregion
    }
    #endregion
    #region SpeechSentenceCls : jedna věta
    /// <summary>
    /// SpeechSentenceCls : jedna věta
    /// </summary>
    public class SpeechSentenceCls : IEnumerable<SpeechWordCls>
    {
        public SpeechSentenceCls()
        {
            this._Words = new List<SpeechWordCls>();
            this.IsCompleted = false;
        }
        public override string ToString()
        {
            string result = "";
            this._Words.ForEach(w => result += w.Text);
            return result;
        }
        public void Add(string text, SpeechWordType wordType)
        {
            this._Words.Add(new SpeechWordCls(text, wordType));
            this.IsCompleted = (wordType == SpeechWordType.BlockDelimiter);
        }
        public void Add(SpeechWordCls word)
        {
            this._Words.Add(word);
            this.IsCompleted = (word.WordType == SpeechWordType.BlockDelimiter);
        }
        /// <summary>
        /// Slova a další prvky této věty
        /// </summary>
        private List<SpeechWordCls> _Words;
        /// <summary>
        /// Počet slov a dalších znaků věty
        /// </summary>
        public int WordCount { get { return this._Words.Count; } }
        /// <summary>
        /// Tato věta je ukončená?
        /// </summary>
        public bool IsCompleted { get; private set; }
        /// <summary>
        /// Poslední slovo, nebo null pokud this.WordCount == 0
        /// </summary>
        public SpeechWordCls LastWord { get { return (this.WordCount > 0 ? this._Words[this.WordCount - 1] : null); } }
        #region IEnumerable Members
        IEnumerator<SpeechWordCls> IEnumerable<SpeechWordCls>.GetEnumerator() { return this._Words.GetEnumerator(); }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this._Words.GetEnumerator(); }
        #endregion
    }
    #endregion
    #region SpeechWordCls : jedno slovo nebo jiný znak
    public class SpeechWordCls
    {
        public SpeechWordCls(string text, SpeechWordType wordType)
        {
            this.Text = text;
            this.WordType = wordType;
        }
        public override string ToString()
        {
            return this.Text;
        }
        public string Text { get; private set; }
        public SpeechWordType WordType { get; private set; }
    }
    public enum SpeechWordType
    {
        Text,
        WordDelimiter,
        BlockDelimiter
    }
    #endregion
    #region SpeechAudioCls
    /// <summary>
    /// SpeechAudioCls
    /// </summary>
    internal class SpeechAudioCls
    {
        //private void speechtest()
        //{
        //    using (System.Speech.Synthesis.SpeechSynthesizer sps = new System.Speech.Synthesis.SpeechSynthesizer())
        //    {
        //        var voices = sps.GetInstalledVoices();
        //        sps.SelectVoice(voices[0].VoiceInfo.Name);
        //        int rate = sps.Rate;
        //        sps.Rate = rate + 2;
        //        sps.AddLexicon(new Uri(@"C:\David\_Cs2010\Neuronic\Neuronic\Data\SpeechGrammarCZ.pls"), "application/pls+xml");
        //        sps.SetOutputToDefaultAudioDevice();
        //        sps.Speak("English speak is currently installed.");
        //    }
        //}
        //private List<string> _SearchServers()
        //{
        //    List<string> result = new List<string>();

        //    string myServer = Environment.MachineName;
        //    System.Data.DataTable servers = SqlDataSourceEnumerator.Instance.GetDataSources();
        //    for (int i = 0; i < servers.Rows.Count; i++)
        //    {
        //        System.Data.DataRow row = servers.Rows[i];

        //        object[] data = row.ItemArray;


        //        string name = (row.IsNull("ServerName") ? null : (string)row["ServerName"]);
        //        string inst = (row.IsNull("InstanceName") ? null : (string)row["InstanceName"]);

        //        if (name == null) name = "";
        //        if (inst == null) inst = "";

        //        if (String.IsNullOrEmpty(inst))
        //            result.Add(name);
        //        else
        //            result.Add(name + "\\" + inst);

        //    }
        //    return result;
        //}    
    }
    #endregion
}
